'use strict';
/**
 * This is the package I have use in two of my previous generator, of course you might not
 * need to use all of them. But its more than enough to get your started.
 */
var fs = require('fs');
var path = require('path');
var util = require('util');
var yeoman = require('yeoman-generator');
var yosay = require('yosay');
var wiredep = require('wiredep');
var chalk = require('chalk');
var glob = require('glob');
var htmlWiring = require("html-wiring");
var isInstalled = require('is-installed');
var ncp = require('ncp').ncp;
    ncp.limit = 16;
var exec = require('child_process').exec;
var _ = require('underscore');
_.mixin(require('underscore.inflections'));
// our lib
var nbUtils = require('../../lib/nb-utils');
var nbDots = require('../../lib/nb-dots');
var nbPreference = require('../../lib/nb-preference');
/**
 * your starting point
 */
 /**
  * Constructor
  */
 var Generator = module.exports = function(args, options)
 {
 	yeoman.generators.Base.apply(this, arguments);
 	// bunch of init setup
 	this.argument('appname', {type: String, required: false });
 	// set this up to hold the answers
 	this.answers = {};
    this.app = {}; // useful for templating later
 	// get app name etc
 	this._getAppName();
 	// setup other options
 	this._setupOptions();
 	// when this end final callback
   	this.on('end', function () {
         this._runFinalSetup();
   	});

   	this.pkg = require('../../package.json');
    this.sourceRoot(path.join(__dirname, '..','templates'));
 };

/**
 * extending base class
 */
util.inherits(Generator, yeoman.generators.Base);

	////////////////////////////////////////
	//	Yeoman call all this public class //
	// one after the other, so you order  //
	// what you need to do, those prefix  //
	// with _ will not get call           //
	////////////////////////////////////////

/**
 * additional code to be call one after the other
 * this whole thing could be removed
 */
Generator.prototype.welcome = function()
{
    var self = this;
    var lang = self.answers.lang;
    var cb = self.async();
    nbPreference.init(lang).then(function(result)
    {
        cb();
        if (!this.options['skip-welcome-message']) {
            var lang = this.env.options.lang;
            var hello = (lang==='en') ? 'Glad I can help, my lord.' : '主人，很荣幸可以为你效劳';
            var appDesc = chalk.white('AmazeUI + React.js');
            var second = chalk.magenta('由') + chalk.white('panesjs.com') + chalk.magenta('提供的 ') + appDesc + chalk.magenta(' 开发協助工具\n');
            if (lang==='en') {
                second = chalk.magenta('Yo Generator for ') + appDesc + chalk.magenta(' brought to you by ') + chalk.white('panesjs.com' + '\n');
            }
        	this.log(yosay(hello));
        	this.log(second);
      	}
    });
};

/**
 * if the user didn't startup the app with appName paramter,
 * then we ask them if they want to change it here
 */
Generator.prototype.ifWantToChangeAppName = function()
{
    var self = this;
	if (self.baseNameOption) {
		var cb = this.async();
		var appName = this.answers.appname;
		var msg = (this.answers.lang==='en') ? 'Your appname is: `' + appName + '`, would you like to change it?'
                                             : '你现时的项目名是:`' + appName + '`, 你想修改吗？';
		this.prompt({
	        type: 'input',
	        name: 'appname',
	        message: msg,
	        default: appName
	    }, function(props) {
	        self._getAppName(props.appName);
	        cb();
	    }.bind(this));
	}
};

/**
 * @TODO Ask if the developer would like to use ES5 or ES6 for development
 */
Generator.prototype.developmentStyle = function()
{
    var self = this;
    var msg1 = (this.answers.lang==='en') ? 'Legacy product will not received update' : '旧的产品将不更新';
    var choices = [
        {value: 'ES5' , name: 'ES5 + Browsersify (.js) [' + msg1 + ']'},
        {value: 'ES6' , name: 'ES6 + Babel + Browsersify (.jsx)'}
    ];
    var cb = self.async();
    var lang = self.answers.lang;
    this.prompt({
        type: 'list',
        name: 'jsVer',
        message: (lang=='en') ? 'Which combo would you like to use?' : '你想用那个版本的套餐?',
        choices: choices,
        default: 'ES5'
    } , function(props)
    {
        self.answers.jsVer = props.jsVer;
        self.answers.scriptSuffix = (props.jsVer==='ES5') ? '.js' : '.jsx';
        cb();
    }.bind(this));
};

    /*************************************
            START GENERATE FILES
    **************************************/

/**
 * generate the root files
 */
Generator.prototype.generateRootFiles = function()
{
    var join = path.join;
    var self = this;
    var root = 'root';
    // copy all the hidden files
    var rootFiles = ['editorconfig' , 'gitattributes' , 'eslintrc' , 'gitignore' , 'bowerrc'];
    self._copyTo(root , rootFiles , '.');

    // setup our own config
    self.template(join(root,'amaze-config.json') , '.amaze-config.json');
    self._copyTemplate(root , ['package.json' , 'bower.json', 'gulpfile.js' , 'README.md']);
    // the yo config file
    // self.config.save();
    self.config.set({'lang': self.lang , 'jsVer': self.jsVer});
};

/**
 * seperate them
 */
Generator.prototype.generateHtmlFiles = function()
{
    var join = path.join;
    var self = this;
    _.each(['robots.txt' , 'favicon.ico' , 'humans.txt' , 'manifest.json' , 'manifest.webapp'] , function(file)
    {
        self.copy(join('common' , file) , join(self.appPath , file));
    });
    // generate the html pages
    this.app.title404 = this.answers.lang === 'cn' ? '' : 'Ooops, sorry page not found';
    self._copyTemplate('common' , ['index.html' , '404.html'] , self.appPath);

    self.copy(join('common' , 'style.less') , join(self.appPath , 'styles' , 'style.less'));
    self.copy(join('common' , 'panesjs.png') , join(self.appPath , 'images' , 'panesjs.png'));
};

/**
 * creating the actually application files
 */
Generator.prototype.generateApplicationFiles = function()
{
    var join = path.join;
    var self = this;
    var root = join('base' , this.answers.jsVer);
    var ext = this.answers.scriptSuffix;
    var scriptBase = join(self.appPath , 'scripts');
    var stockDir = join(root , 'stock');
    var es6 = this.answers.scriptSuffix==='.jsx';

    this.underscore = _;
    this.app.components = {
        'RouteLink': './components/RouteLink',
        'SiteFooter': './components/SiteFooter',
        'AuthenticateWrapper': './components/AuthenticateWrapper'
    };

    this.pageComponents = false;
    this.pageComponent = false;
    this.app.pages = {};
    // creating two more pages
    _.each([1,2] , function(page)
    {
        self.pageName = 'Page' + page;
        self.template(join(root , 'page' + ext) , join(scriptBase , 'pages' , 'page' + page + ext));
        self.app.pages[self.pageName] = './pages/page' + page;
    });
    self.app.pages['Login'] = './pages/login';
    self.template(join(root , 'app' + ext) , join(scriptBase , 'app' + ext));
    // add a login page
    self.pageComponents = {
        'Login': '../components/login'
    };
    self.pageComponent = '<Login></Login>';
    self.template(join(root , 'page' + ext) , join(scriptBase , 'pages' , 'login' + ext));

    // just copy
    _.each(this.app.components , function(p , n)
    {
        self.copy(join(stockDir , n + ext) , join(scriptBase , 'components' , n + ext));
    });
    // the index file is a stock
    self.greetingMsg = (this.answers.lang==='en') ? 'Welcome to the Amaze UI React starter app.' : '欢迎使用 Amaze UI React 入门套件。';
    self.template(join(root , 'stock' , 'index' + ext) , join(scriptBase , 'pages' ,'index' + ext));

    // the following are for the Flux framework files
    // copy dispatchers
    _.each(['AppDispatcher'] , function(file) {
        self.copy(join(stockDir, file + ext) , join(scriptBase , 'dispatchers' , file + ext));
    });
    // copy services
    _.each(['JwtAuthService' , 'RouterContainerService'] , function(file)
    {
        self.copy(join(stockDir , file + ext) , join(scriptBase , 'services' , file.replace('Service' , '') + ext));
    });
    // copy action
    var toCopy = {
        'LoginActions': {dir: 'actions' , name: 'login'},
        'LoginConstants': {dir: 'constants' , name: 'login'},
        'LoginComponent': {dir: 'components' , name: 'login'},
        'LoginStore': {dir: 'stores' , name: 'login'}
    };
    if (es6) {
        toCopy['BaseStore'] = {dir: 'stores' , name: 'base'};
    }

    _.each(toCopy , function(dest , name)
    {
        self.copy(join(stockDir , name + ext) , join(scriptBase , dest.dir , dest.name + ext));
    });

};

	////////////////////////////////
	//       PRIVATE METHODS      //
	////////////////////////////////

Generator.prototype._copyTo = function(root , files , prefix)
{
    var self = this;
    prefix = prefix || '';
    _.each(files , function(file)
    {
        self.copy(path.join(root , file) , prefix + file);
    });
};

Generator.prototype._copyTemplate = function(root , files , _path)
{
    var self = this;
    try {
        _.each(files , function(file)
        {
            var dest = (!_path) ? file : path.join(_path , file);
            self.template(path.join(root , file) , dest);
        });
    } catch (e) {
        self.log.error(root);
        self.log.error(files);
        self.log.error(path);
    }
};

/**
 * getting the appName
 */
Generator.prototype._getAppName = function(appName)
{
  	if (!appName) {
		this.baseNameOption = false;
		if (!this.appname) {
			this.appname = path.basename(process.cwd());
			this.baseNameOption=true;
		}
	}
	else {
		this.appname = appName;
		this.baseNameOption = false;
	}
    this.appTplName =  _.slugify( _.humanize(this.appname) );
    this.scriptAppName = _.camelize(this.appname) + nbUtils.appName(this);
    // the appname got lost somewhere down there.
    this.env.options.appNameAgain  = this.answers.appname = this.appname;
    this.env.options.appTplName    = this.answers.appTplName = this.appTplName;
	this.env.options.scriptAppName = this.answers.scriptAppName = this.scriptAppName;
};

/**
 * setup the other options
 * I break this up here so I could get a clear idea how many options there is (plus those built in ones)
 * so once you have set this up, then you can call it like
 * yo yourTool --option
 */
Generator.prototype._setupOptions = function()
{
	// lang options
    this.option('en' , {
        desc: 'Change to English',
        type: String
    });
    var lang = (this.options['en']) ? 'en' : 'cn';
    this.env.options.lang = this.answers.lang = lang;

    // for setting up the appPath
    this.appPath = this.answers.appPath = this._getPath('appPath' , 'app');
    this.distPath = this.answers.distPath = this._getPath('distPath' , 'dist');
};

/**
 * wrap around a whole bunch of code to get a path
 */
Generator.prototype._getPath = function(propName , defaultPath)
{
    if (typeof this.env.options[propName] === 'undefined') {
        if (!this.env.options[propName]) {
            try {
                this.env.options[propName] = require(path.join(process.cwd() , 'bower.json'))[propName];
            } catch(e) {}
        }
        this.env.options[propName] = this.env.options[propName] || defaultPath;
        this.options[propName] = this.env.options[propName];
    }
    // this.log.error(propName , this.env.options[propName]);
    return this.env.options[propName];
};

/**
 * This is the last method to get call, so you could do clean up or install or whatever here
 */
Generator.prototype._runFinalSetup = function()
{
	// just show you what you can do with the dot thingy
	var self = this;
    var lang = this.answers.lang;
    if (!self.options['skip-install']) {
        var beginning = (lang==='cn') ? '下载中' : 'Downloading';
        var npmCommand = ((lang==='cn' && isInstalled('cnpm')) ? 'cnpm' : 'npm');
        var command = npmCommand + ' install';
        var dotting = new nbDots({beginning: beginning});
        // start running the installation
        exec(npmCommand + ' install' , function(error) {
            dotting.finish();
            if (error) {

                var errorMsg = (lang==='cn') ? '下载出错了 >_< 请再次运行 `' + command + '` 指令'
                                             : 'Package install error, please re-run `' + command + '` again!';
                self.log.error(errorMsg);
                self.log.error(error);
            }
            else {
                var finalMsg = (lang==='cn') ? '任务完成，所有外加插件下载成功。Happy coding!'
                                             : 'Phew, deps are all downloaded. Happy coding!';
                self.log(chalk.yellow(finalMsg));
                self.spawnCommand('gulp' , ['dev']);
                /*
                exec('bower install' , function(error)
                {
                    dotting.finish();
                    if (error) {
                        var errorMsg = (lang==='cn') ? '下载出错了 >_< 请再次运行 `bower install` 指令'
                                                     : 'Package install error, please re-run `bower install` again!';
                        self.log.error(errorMsg);
                        self.log.error(error);
                    }
                    else {

                    }
                });
                */
            }
        });
    }
    else {
        var msg = (lang==='cn') ? 'Please run `npm install` then `gulp dev` to start your development. Happy coding!'
                                : '请先执行 `'+ npmCommand +' install` 然后 `gulp dev` 去开始你的开发工作. Happy coding!';
        self.log(chalk.yellow(msg));
    }
};
